-
Couldn't load subscription status.
- Fork 15k
[clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function #122265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang][Expr] Teach IgnoreUnlessSpelledInSource about implicit calls to std::get free function #122265
Conversation
|
@llvm/pr-subscribers-lldb @llvm/pr-subscribers-clang-codegen Author: Michael Buch (Michael137) ChangesWhen we generate the debug-info for a The heuristic to determine a holding var uses
This patch adjusts our heuristic to account for Fixes #122028 Full diff: https://github.com/llvm/llvm-project/pull/122265.diff 4 Files Affected:
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 560d4ce293365e..e0fdde460c8758 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -85,12 +85,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
if (!Init)
return false;
+ Init = Init->IgnoreUnlessSpelledInSource();
+ if (!Init)
+ return false;
+
+ // For tuple-like decompositions, if the `get` function
+ // is not a member of the decomposed type, but instead a
+ // free function (e.g., how std::get is specialized for
+ // std::pair), then the initializer is a `CallExpr` and
+ // we need to dig into the argument before unwrapping it
+ // with IgnoreUnlessSpelledInSource.
+ if (auto const *CE = llvm::dyn_cast<CallExpr>(Init)) {
+ if (CE->getNumArgs() == 0)
+ return false;
+
+ // The first argument will be the type we're decomposing.
+ // Technically the getter could have more than 1 argument
+ // (e.g., if they're defaulted arguments), but we don't care
+ // about them because we just need to check whether the
+ // first argument is a DecompositionDecl.
+ auto const *Arg = CE->getArg(0);
+ if (!Arg)
+ return false;
+
+ Init = Arg;
+ }
+
auto const *RefExpr =
llvm::dyn_cast_or_null<DeclRefExpr>(Init->IgnoreUnlessSpelledInSource());
if (!RefExpr)
return false;
- return llvm::dyn_cast_or_null<DecompositionDecl>(RefExpr->getDecl());
+ return llvm::isa_and_nonnull<DecompositionDecl>(RefExpr->getDecl());
}
/// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
index 5fbd54c16382c0..55a84a65015842 100644
--- a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
+++ b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple %itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void @llvm.dbg.declare"
+// CHECK: define noundef i32 @_Z1fv
// CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
// CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
// CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], !DIExpression(DW_OP_plus_uconst, 4),
@@ -7,6 +8,9 @@
// CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_4:[0-9]+]], !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 4),
// CHECK: #dbg_declare(ptr %z1, ![[VAR_5:[0-9]+]], !DIExpression()
// CHECK: #dbg_declare(ptr %z2, ![[VAR_6:[0-9]+]], !DIExpression()
+// CHECK: #dbg_declare(ptr %k, ![[VAR_7:[0-9]+]], !DIExpression()
+// CHECK: #dbg_declare(ptr %v, ![[VAR_8:[0-9]+]], !DIExpression()
+// CHECK: #dbg_declare(ptr %w, ![[VAR_9:[0-9]+]], !DIExpression()
// CHECK: ![[VAR_0]] = !DILocalVariable(name: "a"
// CHECK: ![[VAR_1]] = !DILocalVariable(name: "x1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
// CHECK: ![[VAR_2]] = !DILocalVariable(name: "y1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
@@ -14,6 +18,9 @@
// CHECK: ![[VAR_4]] = !DILocalVariable(name: "y2", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
// CHECK: ![[VAR_5]] = !DILocalVariable(name: "z1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
// CHECK: ![[VAR_6]] = !DILocalVariable(name: "z2", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_7]] = !DILocalVariable(name: "k", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_8]] = !DILocalVariable(name: "v", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_9]] = !DILocalVariable(name: "w", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
struct A {
int x;
@@ -39,6 +46,24 @@ struct tuple_size<B> {
};
template<unsigned, typename T> struct tuple_element { using type = int; };
+
+// Decomposition of tuple-like bindings but where the `get` methods
+// are declared as free functions.
+struct triple {
+ int k;
+ int v;
+ int w;
+};
+
+template<>
+struct tuple_size<triple> {
+ static constexpr unsigned value = 3;
+};
+
+template <unsigned I> int get(triple);
+template <> int get<0>(triple p) { return p.k; }
+template <> int get<1>(triple p) { return p.v; }
+template <> int get<2>(triple p) { return p.w; }
} // namespace std
int f() {
@@ -46,5 +71,6 @@ int f() {
auto [x1, y1] = a;
auto &[x2, y2] = a;
auto [z1, z2] = B{1, 2};
- return x1 + y1 + x2 + y2 + z1 + z2;
+ auto [k, v, w] = std::triple{3, 4, 5};
+ return x1 + y1 + x2 + y2 + z1 + z2 + k + v + w;
}
diff --git a/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py b/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py
index 4b26d9f139c01e..cfa01b30068b8f 100644
--- a/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py
+++ b/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py
@@ -98,3 +98,14 @@ def test(self):
self.expect_expr("tx2", result_value="4")
self.expect_expr("ty2", result_value="'z'")
self.expect_expr("tz2", result_value="10")
+
+ self.expect("frame variable",
+ substrs=[
+ "tx1 =",
+ "ty1 =",
+ "tz1 =",
+ "tx2 =",
+ "ty2 =",
+ "tz2 =",
+ "mp1 =",
+ "mp2 ="])
diff --git a/lldb/test/API/lang/cpp/structured-binding/main.cpp b/lldb/test/API/lang/cpp/structured-binding/main.cpp
index 3fbfb18dbeff0c..b649358ebdf667 100644
--- a/lldb/test/API/lang/cpp/structured-binding/main.cpp
+++ b/lldb/test/API/lang/cpp/structured-binding/main.cpp
@@ -1,13 +1,80 @@
// Structured binding in C++ can bind identifiers to subobjects of an object.
//
-// There are three cases we need to test:
+// There are four cases we need to test:
// 1) arrays
-// 2) tuples like objects
-// 3) non-static data members
+// 2) tuple-like objects with `get` member functions
+// 3) tuple-like objects with `get` free functions
+// 4) non-static data members
//
// They can also bind by copy, reference or rvalue reference.
-#include <tuple>
+struct MyPair {
+ int m1;
+ int m2;
+
+ // Helpers to enable tuple-like decomposition.
+ template <unsigned> int get();
+ template <> int get<0>() { return m1; }
+ template <> int get<1>() { return m2; }
+};
+
+namespace std {
+template <typename T1, typename T2, typename T3> struct mock_tuple {
+ T1 m1;
+ T2 m2;
+ T3 m3;
+};
+
+template <typename T> struct tuple_size;
+
+template <unsigned, typename T> struct tuple_element;
+
+// Helpers to enable tuple-like decomposition for MyPair
+template <unsigned I> struct tuple_element<I, MyPair> {
+ using type = int;
+};
+
+template <> struct tuple_size<MyPair> {
+ static constexpr unsigned value = 2;
+};
+
+// Helpers to enable tuple-like decomposition for mock_tuple
+template <typename T1, typename T2, typename T3>
+struct tuple_element<0, mock_tuple<T1, T2, T3>> {
+ using type = T1;
+};
+
+template <typename T1, typename T2, typename T3>
+struct tuple_element<1, mock_tuple<T1, T2, T3>> {
+ using type = T2;
+};
+
+template <typename T1, typename T2, typename T3>
+struct tuple_element<2, mock_tuple<T1, T2, T3>> {
+ using type = T3;
+};
+
+template <typename T1, typename T2, typename T3>
+struct tuple_size<mock_tuple<T1, T2, T3>> {
+ static constexpr unsigned value = 3;
+};
+
+template <unsigned I, typename T1, typename T2, typename T3>
+typename tuple_element<I, mock_tuple<T1, T2, T3>>::type
+get(mock_tuple<T1, T2, T3> p) {
+ switch (I) {
+ case 0:
+ return p.m1;
+ case 1:
+ return p.m2;
+ case 2:
+ return p.m3;
+ default:
+ __builtin_trap();
+ }
+}
+
+} // namespace std
struct A {
int x;
@@ -54,10 +121,12 @@ int main() {
char y{'z'};
int z{10};
- std::tuple<float, char, int> tpl(x, y, z);
+ std::mock_tuple<float, char, int> tpl{.m1 = x, .m2 = y, .m3 = z};
auto [tx1, ty1, tz1] = tpl;
auto &[tx2, ty2, tz2] = tpl;
+ auto [mp1, mp2] = MyPair{.m1 = 1, .m2 = 2};
+
return a1.x + b1 + c1 + d1 + e1 + f1 + a2.y + b2 + c2 + d2 + e2 + f2 + a3.x +
b3 + c3 + d3 + e3 + f3 + carr_copy1 + carr_copy2 + carr_copy3 +
sarr_copy1 + sarr_copy2 + sarr_copy3 + iarr_copy1 + iarr_copy2 +
@@ -65,5 +134,5 @@ int main() {
sarr_ref2 + sarr_ref3 + iarr_ref1 + iarr_ref2 + iarr_ref3 +
carr_rref1 + carr_rref2 + carr_rref3 + sarr_rref1 + sarr_rref2 +
sarr_rref3 + iarr_rref1 + iarr_rref2 + iarr_rref3 + tx1 + ty1 + tz1 +
- tx2 + ty2 + tz2; // break here
+ tx2 + ty2 + tz2 + mp1 + mp2; // break here
}
|
|
@llvm/pr-subscribers-debuginfo Author: Michael Buch (Michael137) ChangesWhen we generate the debug-info for a The heuristic to determine a holding var uses
This patch adjusts our heuristic to account for Fixes #122028 Full diff: https://github.com/llvm/llvm-project/pull/122265.diff 4 Files Affected:
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 560d4ce293365e..e0fdde460c8758 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -85,12 +85,38 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
if (!Init)
return false;
+ Init = Init->IgnoreUnlessSpelledInSource();
+ if (!Init)
+ return false;
+
+ // For tuple-like decompositions, if the `get` function
+ // is not a member of the decomposed type, but instead a
+ // free function (e.g., how std::get is specialized for
+ // std::pair), then the initializer is a `CallExpr` and
+ // we need to dig into the argument before unwrapping it
+ // with IgnoreUnlessSpelledInSource.
+ if (auto const *CE = llvm::dyn_cast<CallExpr>(Init)) {
+ if (CE->getNumArgs() == 0)
+ return false;
+
+ // The first argument will be the type we're decomposing.
+ // Technically the getter could have more than 1 argument
+ // (e.g., if they're defaulted arguments), but we don't care
+ // about them because we just need to check whether the
+ // first argument is a DecompositionDecl.
+ auto const *Arg = CE->getArg(0);
+ if (!Arg)
+ return false;
+
+ Init = Arg;
+ }
+
auto const *RefExpr =
llvm::dyn_cast_or_null<DeclRefExpr>(Init->IgnoreUnlessSpelledInSource());
if (!RefExpr)
return false;
- return llvm::dyn_cast_or_null<DecompositionDecl>(RefExpr->getDecl());
+ return llvm::isa_and_nonnull<DecompositionDecl>(RefExpr->getDecl());
}
/// Returns true if \ref VD is a compiler-generated variable
diff --git a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
index 5fbd54c16382c0..55a84a65015842 100644
--- a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
+++ b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -triple %itanium_abi_triple %s -o - | FileCheck %s --implicit-check-not="call void @llvm.dbg.declare"
+// CHECK: define noundef i32 @_Z1fv
// CHECK: #dbg_declare(ptr %{{[a-z]+}}, ![[VAR_0:[0-9]+]], !DIExpression(),
// CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_1:[0-9]+]], !DIExpression(),
// CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], !DIExpression(DW_OP_plus_uconst, 4),
@@ -7,6 +8,9 @@
// CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_4:[0-9]+]], !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 4),
// CHECK: #dbg_declare(ptr %z1, ![[VAR_5:[0-9]+]], !DIExpression()
// CHECK: #dbg_declare(ptr %z2, ![[VAR_6:[0-9]+]], !DIExpression()
+// CHECK: #dbg_declare(ptr %k, ![[VAR_7:[0-9]+]], !DIExpression()
+// CHECK: #dbg_declare(ptr %v, ![[VAR_8:[0-9]+]], !DIExpression()
+// CHECK: #dbg_declare(ptr %w, ![[VAR_9:[0-9]+]], !DIExpression()
// CHECK: ![[VAR_0]] = !DILocalVariable(name: "a"
// CHECK: ![[VAR_1]] = !DILocalVariable(name: "x1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
// CHECK: ![[VAR_2]] = !DILocalVariable(name: "y1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
@@ -14,6 +18,9 @@
// CHECK: ![[VAR_4]] = !DILocalVariable(name: "y2", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
// CHECK: ![[VAR_5]] = !DILocalVariable(name: "z1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
// CHECK: ![[VAR_6]] = !DILocalVariable(name: "z2", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_7]] = !DILocalVariable(name: "k", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_8]] = !DILocalVariable(name: "v", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_9]] = !DILocalVariable(name: "w", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
struct A {
int x;
@@ -39,6 +46,24 @@ struct tuple_size<B> {
};
template<unsigned, typename T> struct tuple_element { using type = int; };
+
+// Decomposition of tuple-like bindings but where the `get` methods
+// are declared as free functions.
+struct triple {
+ int k;
+ int v;
+ int w;
+};
+
+template<>
+struct tuple_size<triple> {
+ static constexpr unsigned value = 3;
+};
+
+template <unsigned I> int get(triple);
+template <> int get<0>(triple p) { return p.k; }
+template <> int get<1>(triple p) { return p.v; }
+template <> int get<2>(triple p) { return p.w; }
} // namespace std
int f() {
@@ -46,5 +71,6 @@ int f() {
auto [x1, y1] = a;
auto &[x2, y2] = a;
auto [z1, z2] = B{1, 2};
- return x1 + y1 + x2 + y2 + z1 + z2;
+ auto [k, v, w] = std::triple{3, 4, 5};
+ return x1 + y1 + x2 + y2 + z1 + z2 + k + v + w;
}
diff --git a/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py b/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py
index 4b26d9f139c01e..cfa01b30068b8f 100644
--- a/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py
+++ b/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py
@@ -98,3 +98,14 @@ def test(self):
self.expect_expr("tx2", result_value="4")
self.expect_expr("ty2", result_value="'z'")
self.expect_expr("tz2", result_value="10")
+
+ self.expect("frame variable",
+ substrs=[
+ "tx1 =",
+ "ty1 =",
+ "tz1 =",
+ "tx2 =",
+ "ty2 =",
+ "tz2 =",
+ "mp1 =",
+ "mp2 ="])
diff --git a/lldb/test/API/lang/cpp/structured-binding/main.cpp b/lldb/test/API/lang/cpp/structured-binding/main.cpp
index 3fbfb18dbeff0c..b649358ebdf667 100644
--- a/lldb/test/API/lang/cpp/structured-binding/main.cpp
+++ b/lldb/test/API/lang/cpp/structured-binding/main.cpp
@@ -1,13 +1,80 @@
// Structured binding in C++ can bind identifiers to subobjects of an object.
//
-// There are three cases we need to test:
+// There are four cases we need to test:
// 1) arrays
-// 2) tuples like objects
-// 3) non-static data members
+// 2) tuple-like objects with `get` member functions
+// 3) tuple-like objects with `get` free functions
+// 4) non-static data members
//
// They can also bind by copy, reference or rvalue reference.
-#include <tuple>
+struct MyPair {
+ int m1;
+ int m2;
+
+ // Helpers to enable tuple-like decomposition.
+ template <unsigned> int get();
+ template <> int get<0>() { return m1; }
+ template <> int get<1>() { return m2; }
+};
+
+namespace std {
+template <typename T1, typename T2, typename T3> struct mock_tuple {
+ T1 m1;
+ T2 m2;
+ T3 m3;
+};
+
+template <typename T> struct tuple_size;
+
+template <unsigned, typename T> struct tuple_element;
+
+// Helpers to enable tuple-like decomposition for MyPair
+template <unsigned I> struct tuple_element<I, MyPair> {
+ using type = int;
+};
+
+template <> struct tuple_size<MyPair> {
+ static constexpr unsigned value = 2;
+};
+
+// Helpers to enable tuple-like decomposition for mock_tuple
+template <typename T1, typename T2, typename T3>
+struct tuple_element<0, mock_tuple<T1, T2, T3>> {
+ using type = T1;
+};
+
+template <typename T1, typename T2, typename T3>
+struct tuple_element<1, mock_tuple<T1, T2, T3>> {
+ using type = T2;
+};
+
+template <typename T1, typename T2, typename T3>
+struct tuple_element<2, mock_tuple<T1, T2, T3>> {
+ using type = T3;
+};
+
+template <typename T1, typename T2, typename T3>
+struct tuple_size<mock_tuple<T1, T2, T3>> {
+ static constexpr unsigned value = 3;
+};
+
+template <unsigned I, typename T1, typename T2, typename T3>
+typename tuple_element<I, mock_tuple<T1, T2, T3>>::type
+get(mock_tuple<T1, T2, T3> p) {
+ switch (I) {
+ case 0:
+ return p.m1;
+ case 1:
+ return p.m2;
+ case 2:
+ return p.m3;
+ default:
+ __builtin_trap();
+ }
+}
+
+} // namespace std
struct A {
int x;
@@ -54,10 +121,12 @@ int main() {
char y{'z'};
int z{10};
- std::tuple<float, char, int> tpl(x, y, z);
+ std::mock_tuple<float, char, int> tpl{.m1 = x, .m2 = y, .m3 = z};
auto [tx1, ty1, tz1] = tpl;
auto &[tx2, ty2, tz2] = tpl;
+ auto [mp1, mp2] = MyPair{.m1 = 1, .m2 = 2};
+
return a1.x + b1 + c1 + d1 + e1 + f1 + a2.y + b2 + c2 + d2 + e2 + f2 + a3.x +
b3 + c3 + d3 + e3 + f3 + carr_copy1 + carr_copy2 + carr_copy3 +
sarr_copy1 + sarr_copy2 + sarr_copy3 + iarr_copy1 + iarr_copy2 +
@@ -65,5 +134,5 @@ int main() {
sarr_ref2 + sarr_ref3 + iarr_ref1 + iarr_ref2 + iarr_ref3 +
carr_rref1 + carr_rref2 + carr_rref3 + sarr_rref1 + sarr_rref2 +
sarr_rref3 + iarr_rref1 + iarr_rref2 + iarr_rref3 + tx1 + ty1 + tz1 +
- tx2 + ty2 + tz2; // break here
+ tx2 + ty2 + tz2 + mp1 + mp2; // break here
}
|
|
CC'd @AaronBallman and @cor3ntin to sanity check my assumptions about structured bindings here (and how |
| // | ||
| // They can also bind by copy, reference or rvalue reference. | ||
|
|
||
| #include <tuple> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed in favour of mock_tuple so we can better control what kind of tuple-like decomposition we're testing here.
|
✅ With the latest revision this PR passed the Python code formatter. |
|
Did you try to modify |
Yea I was thinking about it initially. But I wasn't sure what that would look like tbh. IIUC |
|
Do we need a test case about explicit object member function template<unsigned> int get(this triple);
template<> int get<0>(this triple t) { return /* ... */; } |
same thing we do for constructor ? Note that I think it would only be useful to |
Hah good point! That's pretty much exactly the heuristic we want. Let me try this
Good question. I think it's worth having one |
15c529b to
60cfb2c
Compare
Updated to use this approach in latest commit. Seems to work well (including the explicit object parameter case). Didn't find a great way to test this in the clang AST unit-tests yet. Trying to understand how the other |
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
|
Added tests and updated PR description. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo comment request.
Thanks
|
|
||
| auto IgnoreImplicitCallSingleStep = [](Expr *E) { | ||
| if (auto *C = dyn_cast<CallExpr>(E)) { | ||
| auto NumArgs = C->getNumArgs(); | ||
| if (NumArgs == 1 || | ||
| (NumArgs > 1 && isa<CXXDefaultArgExpr>(C->getArg(1)))) { | ||
| Expr *A = C->getArg(0); | ||
| if (A->getSourceRange() == E->getSourceRange()) | ||
| return A; | ||
| } | ||
| } | ||
| return E; | ||
| }; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment explaining it's used for std::get/structured binding?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
Hmm there's a clang-tidy test failing because this codepath gets hit for Adding a special case for |
… for std::get free function
When we generate the debug-info for a `VarDecl` we try
to determine whether it was introduced as part of a structure
binding (aka a "holding var"). If it was,
we don't mark it as `artificial`.
The heuristic to determine a holding var uses
`IgnoreUnlessSpelledInSource` to unwrap the `VarDecl` initializer
until we hit a `DeclRefExpr` that refers to a `Decomposition`.
For "tuple-like decompositions", Clang will generate a call to
a `template<size_t I> Foo get(Bar)` function that retrieves the
`Ith` element from the tuple-like structure. If that function is a
member function, we get an AST that looks as follows:
```
VarDecl implicit used z1 'std::tuple_element<0, B>::type &&' cinit
`-ExprWithCleanups <col:10> 'int' xvalue
`-MaterializeTemporaryExpr <col:10> 'int' xvalue extended by Var 0x11d110cf8 'z1' 'std::tuple_element<0, B>::type &&'
`-CXXMemberCallExpr <col:10> 'int'
`-MemberExpr <col:10> '<bound member function type>' .get 0x11d104390
`-ImplicitCastExpr <col:10> 'B' xvalue <NoOp>
`-DeclRefExpr <col:10> 'B' lvalue Decomposition 0x11d1100a8 '' 'B'
```
`IgnoreUnlessSpelledInSource` happily unwraps this down to the
`DeclRefExpr`. However, when the `get` helper is a free function
(which it is for `std::pair` in libc++ for example), then the AST
is:
```
VarDecl col:16 implicit used k 'std::tuple_element<0, const std::tuple<int, int>>::type &' cinit
`-CallExpr <col:16> 'const typename tuple_element<0UL, tuple<int, int>>::type':'const int' lvalue adl
|-ImplicitCastExpr <col:16> 'const typename tuple_element<0UL, tuple<int, int>>::type &(*)(const tuple<int, int> &) noexcept' <FunctionToPointerDecay>
| `-DeclRefExpr <col:16> 'const typename tuple_element<0UL, tuple<int, int>>::type &(const tuple<int, int> &) noexcept' lvalue Function 0x1210262d8 'get' 'const typename tuple_element<0UL, tuple<int, int>>::type &(const tuple<int, int> &) noexcept' (FunctionTemplate 0x11d068088 'get')
`-DeclRefExpr <col:16> 'const std::tuple<int, int>' lvalue Decomposition 0x121021518 '' 'const std::tuple<int, int> &'
```
`IgnoreUnlessSpelledInSource` doesn't unwrap this `CallExpr`, so we
incorrectly mark the binding as `artificial` in debug-info.
This patch adjusts our heuristic to account for `CallExpr` nodes.
Fixes llvm#122028
0fdd5b9 to
549d18c
Compare
|
I'm still happy with this. I did not notice it was not merged |
Yea I was just clearing out my open PRs and noticed this was still open. There is still that clang-tidy failure I haven't figured out (#122265 (comment)). Might need a more targeted heuristic for expressions coming from binding decls. |
|
Latest commit narrows the heuristics so it specifically applies to What my latest change does is try and get to that Let me know what you think @cor3ntin @AaronBallman. There might be a better way of doing this. Particularly, I wasn't sure how to unwrap the |
|
gentle ping |
|
any objections to latest version @cor3ntin ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just as few nits (sorry for the delay and thanks for the pings!)
clang/lib/AST/Expr.cpp
Outdated
| /// for structured bindings. | ||
| bool IsDecompositionDeclRefExpr(const Expr *E) { | ||
| const Expr *Unrwapped = E->IgnoreUnlessSpelledInSource(); | ||
| const DeclRefExpr *Ref = llvm::dyn_cast_or_null<DeclRefExpr>(Unrwapped); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const DeclRefExpr *Ref = llvm::dyn_cast_or_null<DeclRefExpr>(Unrwapped); | |
| const auto *Ref = dyn_cast_or_null<DeclRefExpr>(Unrwapped); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think E can be null, so Unwrapped probably cannot be null either, it should just be dyn_cast
clang/lib/AST/Expr.cpp
Outdated
| if (!Ref) | ||
| return false; | ||
|
|
||
| return llvm::isa_and_nonnull<DecompositionDecl>(Ref->getDecl()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| return llvm::isa_and_nonnull<DecompositionDecl>(Ref->getDecl()); | |
| return isa_and_nonnull<DecompositionDecl>(Ref->getDecl()); |
clang/lib/AST/Expr.cpp
Outdated
| /// a DecompositionDecl. Used to skip Clang-generated calls to std::get | ||
| /// for structured bindings. | ||
| bool IsDecompositionDeclRefExpr(const Expr *E) { | ||
| const Expr *Unrwapped = E->IgnoreUnlessSpelledInSource(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| const Expr *Unrwapped = E->IgnoreUnlessSpelledInSource(); | |
| const Expr *Unwrapped = E->IgnoreUnlessSpelledInSource(); |
clang/lib/AST/Expr.cpp
Outdated
| /// Helper to determine wether \c E is a CXXConstructExpr constructing | ||
| /// a DecompositionDecl. Used to skip Clang-generated calls to std::get | ||
| /// for structured bindings. | ||
| bool IsDecompositionDeclRefExpr(const Expr *E) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd use static rather than an anonymous namespace here
No worries! Thanks for the review |
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/51/builds/23865 Here is the relevant piece of the build log for the reference |
|
I'm afraid I'm seeing a test regression with this change (confirmed via bisect), on Gentoo amd64: |
Thanks for reporting. Hmm looks like on this target the IR variable names just differ slightly. We expect |
|
@mgorny Any chance you have access to the full IR when compiling it with the clang on that bot? |
|
Sure: ; ModuleID = '/var/tmp/portage/llvm-core/clang-22.0.0.9999/work/clang/test/DebugInfo/CXX/structured-binding.cpp'
source_filename = "/var/tmp/portage/llvm-core/clang-22.0.0.9999/work/clang/test/DebugInfo/CXX/structured-binding.cpp"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686-pc-linux-gnu"
%struct.A = type { i32, i32 }
%struct.B = type { i32, i32 }
%"struct.std::triple" = type { i32, i32, i32 }
%struct.C = type { i32, i32 }
%struct.D = type { i32, i32 }
$_ZN1B3getILi0EEEiv = comdat any
$_ZN1B3getILi1EEEiv = comdat any
$_ZNH1C3getILi0EEEiOS_ = comdat any
$_ZNH1C3getILi1EEEiOS_ = comdat any
$_ZN1D3getILi0EEEii = comdat any
$_ZN1D3getILi1EEEii = comdat any
@__const._Z1fv.a = private unnamed_addr constant %struct.A { i32 10, i32 20 }, align 4
@__const._Z1fv. = private unnamed_addr constant %struct.B { i32 1, i32 2 }, align 4
@__const._Z1fv.array = private unnamed_addr constant [2 x i32] [i32 3, i32 4], align 4
@__const._Z1fv..1 = private unnamed_addr constant %"struct.std::triple" { i32 3, i32 4, i32 5 }, align 4
@__const._Z1fv..2 = private unnamed_addr constant %struct.C { i32 2, i32 3 }, align 4
@__const._Z1fv..3 = private unnamed_addr constant %struct.D { i32 2, i32 3 }, align 4
; Function Attrs: mustprogress noinline nounwind optnone
define dso_local noundef i32 @_ZSt3getILj0EEiSt6triple(i32 %p.0, i32 %p.1, i32 %p.2) #0 !dbg !46 {
entry:
%p = alloca %"struct.std::triple", align 4
%k = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 0
store i32 %p.0, ptr %k, align 4
%v = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 1
store i32 %p.1, ptr %v, align 4
%w = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 2
store i32 %p.2, ptr %w, align 4
#dbg_declare(ptr %p, !53, !DIExpression(), !54)
%k1 = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 0, !dbg !55
%0 = load i32, ptr %k1, align 4, !dbg !55
ret i32 %0, !dbg !56
}
; Function Attrs: mustprogress noinline nounwind optnone
define dso_local noundef i32 @_ZSt3getILj1EEiSt6triple(i32 %p.0, i32 %p.1, i32 %p.2) #0 !dbg !57 {
entry:
%p = alloca %"struct.std::triple", align 4
%k = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 0
store i32 %p.0, ptr %k, align 4
%v = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 1
store i32 %p.1, ptr %v, align 4
%w = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 2
store i32 %p.2, ptr %w, align 4
#dbg_declare(ptr %p, !60, !DIExpression(), !61)
%v1 = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 1, !dbg !62
%0 = load i32, ptr %v1, align 4, !dbg !62
ret i32 %0, !dbg !63
}
; Function Attrs: mustprogress noinline nounwind optnone
define dso_local noundef i32 @_ZSt3getILj2EEiSt6triple(i32 %p.0, i32 %p.1, i32 %p.2) #0 !dbg !64 {
entry:
%p = alloca %"struct.std::triple", align 4
%k = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 0
store i32 %p.0, ptr %k, align 4
%v = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 1
store i32 %p.1, ptr %v, align 4
%w = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 2
store i32 %p.2, ptr %w, align 4
#dbg_declare(ptr %p, !67, !DIExpression(), !68)
%w1 = getelementptr inbounds nuw %"struct.std::triple", ptr %p, i32 0, i32 2, !dbg !69
%0 = load i32, ptr %w1, align 4, !dbg !69
ret i32 %0, !dbg !70
}
; Function Attrs: mustprogress noinline nounwind optnone
define dso_local noundef i32 @_Z1fv() #0 !dbg !71 {
entry:
%a = alloca %struct.A, align 4
%0 = alloca %struct.A, align 4
%1 = alloca ptr, align 4
%2 = alloca %struct.B, align 4
%z1 = alloca ptr, align 4
%ref.tmp = alloca i32, align 4
%z2 = alloca ptr, align 4
%ref.tmp1 = alloca i32, align 4
%array = alloca [2 x i32], align 4
%3 = alloca ptr, align 4
%cmplx = alloca { i32, i32 }, align 4
%4 = alloca ptr, align 4
%vctr = alloca <2 x i32>, align 8
%5 = alloca ptr, align 4
%6 = alloca %"struct.std::triple", align 4
%k = alloca ptr, align 4
%ref.tmp3 = alloca i32, align 4
%agg.tmp = alloca %"struct.std::triple", align 4
%v6 = alloca ptr, align 4
%ref.tmp7 = alloca i32, align 4
%agg.tmp8 = alloca %"struct.std::triple", align 4
%w13 = alloca ptr, align 4
%ref.tmp14 = alloca i32, align 4
%agg.tmp15 = alloca %"struct.std::triple", align 4
%7 = alloca %struct.C, align 4
%m = alloca ptr, align 4
%ref.tmp20 = alloca i32, align 4
%n = alloca ptr, align 4
%ref.tmp22 = alloca i32, align 4
%8 = alloca %struct.D, align 4
%s = alloca ptr, align 4
%ref.tmp24 = alloca i32, align 4
%p = alloca ptr, align 4
%ref.tmp26 = alloca i32, align 4
#dbg_declare(ptr %a, !74, !DIExpression(), !79)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %a, ptr align 4 @__const._Z1fv.a, i32 8, i1 false), !dbg !79
#dbg_declare(ptr %0, !80, !DIExpression(), !81)
#dbg_declare(ptr %0, !82, !DIExpression(DW_OP_plus_uconst, 4), !83)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %0, ptr align 4 %a, i32 8, i1 false), !dbg !84
#dbg_declare(ptr %1, !85, !DIExpression(DW_OP_deref), !86)
#dbg_declare(ptr %1, !87, !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 4), !88)
store ptr %a, ptr %1, align 4, !dbg !89
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %2, ptr align 4 @__const._Z1fv., i32 8, i1 false), !dbg !90
#dbg_declare(ptr %z1, !91, !DIExpression(), !98)
%call = call noundef i32 @_ZN1B3getILi0EEEiv(ptr noundef nonnull align 4 dereferenceable(8) %2), !dbg !90
store i32 %call, ptr %ref.tmp, align 4, !dbg !98
store ptr %ref.tmp, ptr %z1, align 4, !dbg !98
#dbg_declare(ptr %z2, !99, !DIExpression(), !105)
%call2 = call noundef i32 @_ZN1B3getILi1EEEiv(ptr noundef nonnull align 4 dereferenceable(8) %2), !dbg !90
store i32 %call2, ptr %ref.tmp1, align 4, !dbg !105
store ptr %ref.tmp1, ptr %z2, align 4, !dbg !105
#dbg_declare(ptr %array, !106, !DIExpression(), !110)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %array, ptr align 4 @__const._Z1fv.array, i32 8, i1 false), !dbg !110
#dbg_declare(ptr %3, !111, !DIExpression(DW_OP_deref), !112)
#dbg_declare(ptr %3, !113, !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 4), !114)
store ptr %array, ptr %3, align 4, !dbg !115
#dbg_declare(ptr %cmplx, !116, !DIExpression(), !118)
%cmplx.realp = getelementptr inbounds nuw { i32, i32 }, ptr %cmplx, i32 0, i32 0, !dbg !118
%cmplx.imagp = getelementptr inbounds nuw { i32, i32 }, ptr %cmplx, i32 0, i32 1, !dbg !118
store i32 1, ptr %cmplx.realp, align 4, !dbg !118
store i32 2, ptr %cmplx.imagp, align 4, !dbg !118
#dbg_declare(ptr %4, !119, !DIExpression(DW_OP_deref), !120)
#dbg_declare(ptr %4, !121, !DIExpression(DW_OP_deref), !122)
store ptr %cmplx, ptr %4, align 4, !dbg !123
#dbg_declare(ptr %vctr, !124, !DIExpression(), !126)
store <2 x i32> <i32 1, i32 2>, ptr %vctr, align 8, !dbg !126
#dbg_declare(ptr %5, !127, !DIExpression(DW_OP_deref), !128)
#dbg_declare(ptr %5, !129, !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 4), !130)
store ptr %vctr, ptr %5, align 4, !dbg !131
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %6, ptr align 4 @__const._Z1fv..1, i32 12, i1 false), !dbg !132
#dbg_declare(ptr %k, !133, !DIExpression(), !139)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr align 4 %6, i32 12, i1 false), !dbg !139
%k4 = getelementptr inbounds nuw %"struct.std::triple", ptr %agg.tmp, i32 0, i32 0, !dbg !139
%9 = load i32, ptr %k4, align 4, !dbg !139
%v = getelementptr inbounds nuw %"struct.std::triple", ptr %agg.tmp, i32 0, i32 1, !dbg !139
%10 = load i32, ptr %v, align 4, !dbg !139
%w = getelementptr inbounds nuw %"struct.std::triple", ptr %agg.tmp, i32 0, i32 2, !dbg !139
%11 = load i32, ptr %w, align 4, !dbg !139
%call5 = call noundef i32 @_ZSt3getILj0EEiSt6triple(i32 %9, i32 %10, i32 %11), !dbg !139
store i32 %call5, ptr %ref.tmp3, align 4, !dbg !139
store ptr %ref.tmp3, ptr %k, align 4, !dbg !139
#dbg_declare(ptr %v6, !140, !DIExpression(), !145)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp8, ptr align 4 %6, i32 12, i1 false), !dbg !145
%k9 = getelementptr inbounds nuw %"struct.std::triple", ptr %agg.tmp8, i32 0, i32 0, !dbg !145
%12 = load i32, ptr %k9, align 4, !dbg !145
%v10 = getelementptr inbounds nuw %"struct.std::triple", ptr %agg.tmp8, i32 0, i32 1, !dbg !145
%13 = load i32, ptr %v10, align 4, !dbg !145
%w11 = getelementptr inbounds nuw %"struct.std::triple", ptr %agg.tmp8, i32 0, i32 2, !dbg !145
%14 = load i32, ptr %w11, align 4, !dbg !145
%call12 = call noundef i32 @_ZSt3getILj1EEiSt6triple(i32 %12, i32 %13, i32 %14), !dbg !145
store i32 %call12, ptr %ref.tmp7, align 4, !dbg !145
store ptr %ref.tmp7, ptr %v6, align 4, !dbg !145
#dbg_declare(ptr %w13, !146, !DIExpression(), !152)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp15, ptr align 4 %6, i32 12, i1 false), !dbg !152
%k16 = getelementptr inbounds nuw %"struct.std::triple", ptr %agg.tmp15, i32 0, i32 0, !dbg !152
%15 = load i32, ptr %k16, align 4, !dbg !152
%v17 = getelementptr inbounds nuw %"struct.std::triple", ptr %agg.tmp15, i32 0, i32 1, !dbg !152
%16 = load i32, ptr %v17, align 4, !dbg !152
%w18 = getelementptr inbounds nuw %"struct.std::triple", ptr %agg.tmp15, i32 0, i32 2, !dbg !152
%17 = load i32, ptr %w18, align 4, !dbg !152
%call19 = call noundef i32 @_ZSt3getILj2EEiSt6triple(i32 %15, i32 %16, i32 %17), !dbg !152
store i32 %call19, ptr %ref.tmp14, align 4, !dbg !152
store ptr %ref.tmp14, ptr %w13, align 4, !dbg !152
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %7, ptr align 4 @__const._Z1fv..2, i32 8, i1 false), !dbg !153
#dbg_declare(ptr %m, !154, !DIExpression(), !160)
%call21 = call noundef i32 @_ZNH1C3getILi0EEEiOS_(ptr noundef nonnull align 4 dereferenceable(8) %7), !dbg !160
store i32 %call21, ptr %ref.tmp20, align 4, !dbg !160
store ptr %ref.tmp20, ptr %m, align 4, !dbg !160
#dbg_declare(ptr %n, !161, !DIExpression(), !166)
%call23 = call noundef i32 @_ZNH1C3getILi1EEEiOS_(ptr noundef nonnull align 4 dereferenceable(8) %7), !dbg !166
store i32 %call23, ptr %ref.tmp22, align 4, !dbg !166
store ptr %ref.tmp22, ptr %n, align 4, !dbg !166
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %8, ptr align 4 @__const._Z1fv..3, i32 8, i1 false), !dbg !167
#dbg_declare(ptr %s, !168, !DIExpression(), !174)
%call25 = call noundef i32 @_ZN1D3getILi0EEEii(ptr noundef nonnull align 4 dereferenceable(8) %8, i32 noundef 0), !dbg !167
store i32 %call25, ptr %ref.tmp24, align 4, !dbg !174
store ptr %ref.tmp24, ptr %s, align 4, !dbg !174
#dbg_declare(ptr %p, !175, !DIExpression(), !180)
%call27 = call noundef i32 @_ZN1D3getILi1EEEii(ptr noundef nonnull align 4 dereferenceable(8) %8, i32 noundef 0), !dbg !167
store i32 %call27, ptr %ref.tmp26, align 4, !dbg !180
store ptr %ref.tmp26, ptr %p, align 4, !dbg !180
%x = getelementptr inbounds nuw %struct.A, ptr %0, i32 0, i32 0, !dbg !181
%18 = load i32, ptr %x, align 4, !dbg !181
%y = getelementptr inbounds nuw %struct.A, ptr %0, i32 0, i32 1, !dbg !182
%19 = load i32, ptr %y, align 4, !dbg !182
%add = add nsw i32 %18, %19, !dbg !183
%20 = load ptr, ptr %1, align 4, !dbg !184, !nonnull !49, !align !185
%x28 = getelementptr inbounds nuw %struct.A, ptr %20, i32 0, i32 0, !dbg !184
%21 = load i32, ptr %x28, align 4, !dbg !184
%add29 = add nsw i32 %add, %21, !dbg !186
%22 = load ptr, ptr %1, align 4, !dbg !187, !nonnull !49, !align !185
%y30 = getelementptr inbounds nuw %struct.A, ptr %22, i32 0, i32 1, !dbg !187
%23 = load i32, ptr %y30, align 4, !dbg !187
%add31 = add nsw i32 %add29, %23, !dbg !188
%24 = load ptr, ptr %z1, align 4, !dbg !189, !nonnull !49, !align !185
%25 = load i32, ptr %24, align 4, !dbg !189
%add32 = add nsw i32 %add31, %25, !dbg !190
%26 = load ptr, ptr %z2, align 4, !dbg !191, !nonnull !49, !align !185
%27 = load i32, ptr %26, align 4, !dbg !191
%add33 = add nsw i32 %add32, %27, !dbg !192
%28 = load ptr, ptr %3, align 4, !dbg !193, !nonnull !49, !align !185
%arrayidx = getelementptr inbounds [2 x i32], ptr %28, i32 0, i32 0, !dbg !193
%29 = load i32, ptr %arrayidx, align 4, !dbg !193
%add34 = add nsw i32 %add33, %29, !dbg !194
%30 = load ptr, ptr %3, align 4, !dbg !195, !nonnull !49, !align !185
%arrayidx35 = getelementptr inbounds [2 x i32], ptr %30, i32 0, i32 1, !dbg !195
%31 = load i32, ptr %arrayidx35, align 4, !dbg !195
%add36 = add nsw i32 %add34, %31, !dbg !196
%32 = load ptr, ptr %4, align 4, !dbg !197, !nonnull !49, !align !185
%.realp = getelementptr inbounds nuw { i32, i32 }, ptr %32, i32 0, i32 0, !dbg !197
%33 = load i32, ptr %.realp, align 4, !dbg !197
%add37 = add nsw i32 %add36, %33, !dbg !198
%34 = load ptr, ptr %4, align 4, !dbg !199, !nonnull !49, !align !185
%.imagp = getelementptr inbounds nuw { i32, i32 }, ptr %34, i32 0, i32 1, !dbg !199
%35 = load i32, ptr %.imagp, align 4, !dbg !199
%add38 = add nsw i32 %add37, %35, !dbg !200
%36 = load ptr, ptr %5, align 4, !dbg !201, !nonnull !49, !align !202
%37 = load <2 x i32>, ptr %36, align 8, !dbg !201
%vecext = extractelement <2 x i32> %37, i32 0, !dbg !201
%add39 = add nsw i32 %add38, %vecext, !dbg !203
%38 = load ptr, ptr %5, align 4, !dbg !204, !nonnull !49, !align !202
%39 = load <2 x i32>, ptr %38, align 8, !dbg !204
%vecext40 = extractelement <2 x i32> %39, i32 1, !dbg !204
%add41 = add nsw i32 %add39, %vecext40, !dbg !205
ret i32 %add41, !dbg !206
}
; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i32(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i32, i1 immarg) #1
; Function Attrs: mustprogress noinline nounwind optnone
define linkonce_odr noundef i32 @_ZN1B3getILi0EEEiv(ptr noundef nonnull align 4 dereferenceable(8) %this) #0 comdat align 2 !dbg !207 {
entry:
%this.addr = alloca ptr, align 4
store ptr %this, ptr %this.addr, align 4
#dbg_declare(ptr %this.addr, !208, !DIExpression(), !210)
%this1 = load ptr, ptr %this.addr, align 4
%w = getelementptr inbounds nuw %struct.B, ptr %this1, i32 0, i32 0, !dbg !211
%0 = load i32, ptr %w, align 4, !dbg !211
ret i32 %0, !dbg !212
}
; Function Attrs: mustprogress noinline nounwind optnone
define linkonce_odr noundef i32 @_ZN1B3getILi1EEEiv(ptr noundef nonnull align 4 dereferenceable(8) %this) #0 comdat align 2 !dbg !213 {
entry:
%this.addr = alloca ptr, align 4
store ptr %this, ptr %this.addr, align 4
#dbg_declare(ptr %this.addr, !214, !DIExpression(), !215)
%this1 = load ptr, ptr %this.addr, align 4
%z = getelementptr inbounds nuw %struct.B, ptr %this1, i32 0, i32 1, !dbg !216
%0 = load i32, ptr %z, align 4, !dbg !216
ret i32 %0, !dbg !217
}
; Function Attrs: mustprogress noinline nounwind optnone
define linkonce_odr noundef i32 @_ZNH1C3getILi0EEEiOS_(ptr noundef nonnull align 4 dereferenceable(8) %self) #0 comdat align 2 !dbg !218 {
entry:
%self.addr = alloca ptr, align 4
store ptr %self, ptr %self.addr, align 4
#dbg_declare(ptr %self.addr, !219, !DIExpression(), !221)
%0 = load ptr, ptr %self.addr, align 4, !dbg !222, !nonnull !49, !align !185
%w = getelementptr inbounds nuw %struct.C, ptr %0, i32 0, i32 0, !dbg !223
%1 = load i32, ptr %w, align 4, !dbg !223
ret i32 %1, !dbg !224
}
; Function Attrs: mustprogress noinline nounwind optnone
define linkonce_odr noundef i32 @_ZNH1C3getILi1EEEiOS_(ptr noundef nonnull align 4 dereferenceable(8) %self) #0 comdat align 2 !dbg !225 {
entry:
%self.addr = alloca ptr, align 4
store ptr %self, ptr %self.addr, align 4
#dbg_declare(ptr %self.addr, !226, !DIExpression(), !227)
%0 = load ptr, ptr %self.addr, align 4, !dbg !228, !nonnull !49, !align !185
%z = getelementptr inbounds nuw %struct.C, ptr %0, i32 0, i32 1, !dbg !229
%1 = load i32, ptr %z, align 4, !dbg !229
ret i32 %1, !dbg !230
}
; Function Attrs: mustprogress noinline nounwind optnone
define linkonce_odr noundef i32 @_ZN1D3getILi0EEEii(ptr noundef nonnull align 4 dereferenceable(8) %this, i32 noundef %unused) #0 comdat align 2 !dbg !231 {
entry:
%this.addr = alloca ptr, align 4
%unused.addr = alloca i32, align 4
store ptr %this, ptr %this.addr, align 4
#dbg_declare(ptr %this.addr, !232, !DIExpression(), !234)
store i32 %unused, ptr %unused.addr, align 4
#dbg_declare(ptr %unused.addr, !235, !DIExpression(), !236)
%this1 = load ptr, ptr %this.addr, align 4
%w = getelementptr inbounds nuw %struct.D, ptr %this1, i32 0, i32 0, !dbg !237
%0 = load i32, ptr %w, align 4, !dbg !237
ret i32 %0, !dbg !238
}
; Function Attrs: mustprogress noinline nounwind optnone
define linkonce_odr noundef i32 @_ZN1D3getILi1EEEii(ptr noundef nonnull align 4 dereferenceable(8) %this, i32 noundef %unused) #0 comdat align 2 !dbg !239 {
entry:
%this.addr = alloca ptr, align 4
%unused.addr = alloca i32, align 4
store ptr %this, ptr %this.addr, align 4
#dbg_declare(ptr %this.addr, !240, !DIExpression(), !241)
store i32 %unused, ptr %unused.addr, align 4
#dbg_declare(ptr %unused.addr, !242, !DIExpression(), !243)
%this1 = load ptr, ptr %this.addr, align 4
%z = getelementptr inbounds nuw %struct.D, ptr %this1, i32 0, i32 1, !dbg !244
%0 = load i32, ptr %z, align 4, !dbg !244
ret i32 %0, !dbg !245
}
attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+x87" }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!42, !43, !44}
!llvm.ident = !{!45}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 22.0.0git012680fa", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "/var/tmp/portage/llvm-core/clang-22.0.0.9999/work/clang/test/DebugInfo/CXX/<stdin>", directory: "")
!2 = !{!3, !18, !24, !33}
!3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "B", file: !4, line: 44, size: 64, flags: DIFlagTypePassByValue, elements: !5, identifier: "_ZTS1B")
!4 = !DIFile(filename: "/var/tmp/portage/llvm-core/clang-22.0.0.9999/work/clang/test/DebugInfo/CXX/structured-binding.cpp", directory: "")
!5 = !{!6, !8, !9, !15}
!6 = !DIDerivedType(tag: DW_TAG_member, name: "w", scope: !3, file: !4, line: 45, baseType: !7, size: 32)
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!8 = !DIDerivedType(tag: DW_TAG_member, name: "z", scope: !3, file: !4, line: 46, baseType: !7, size: 32, offset: 32)
!9 = !DISubprogram(name: "get<0>", linkageName: "_ZN1B3getILi0EEEiv", scope: !3, file: !4, line: 48, type: !10, scopeLine: 48, flags: DIFlagPrototyped, spFlags: 0, templateParams: !13)
!10 = !DISubroutineType(types: !11)
!11 = !{!7, !12}
!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 32, flags: DIFlagArtificial | DIFlagObjectPointer)
!13 = !{!14}
!14 = !DITemplateValueParameter(type: !7, value: i32 0)
!15 = !DISubprogram(name: "get<1>", linkageName: "_ZN1B3getILi1EEEiv", scope: !3, file: !4, line: 49, type: !10, scopeLine: 49, flags: DIFlagPrototyped, spFlags: 0, templateParams: !16)
!16 = !{!17}
!17 = !DITemplateValueParameter(type: !7, value: i32 1)
!18 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "triple", scope: !19, file: !4, line: 92, size: 96, flags: DIFlagTypePassByValue, elements: !20, identifier: "_ZTSSt6triple")
!19 = !DINamespace(name: "std", scope: null)
!20 = !{!21, !22, !23}
!21 = !DIDerivedType(tag: DW_TAG_member, name: "k", scope: !18, file: !4, line: 93, baseType: !7, size: 32)
!22 = !DIDerivedType(tag: DW_TAG_member, name: "v", scope: !18, file: !4, line: 94, baseType: !7, size: 32, offset: 32)
!23 = !DIDerivedType(tag: DW_TAG_member, name: "w", scope: !18, file: !4, line: 95, baseType: !7, size: 32, offset: 64)
!24 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C", file: !4, line: 52, size: 64, flags: DIFlagTypePassByValue, elements: !25, identifier: "_ZTS1C")
!25 = !{!26, !27, !28, !32}
!26 = !DIDerivedType(tag: DW_TAG_member, name: "w", scope: !24, file: !4, line: 53, baseType: !7, size: 32)
!27 = !DIDerivedType(tag: DW_TAG_member, name: "z", scope: !24, file: !4, line: 54, baseType: !7, size: 32, offset: 32)
!28 = !DISubprogram(name: "get<0>", linkageName: "_ZNH1C3getILi0EEEiOS_", scope: !24, file: !4, line: 56, type: !29, scopeLine: 56, flags: DIFlagPrototyped, spFlags: 0, templateParams: !13)
!29 = !DISubroutineType(types: !30)
!30 = !{!7, !31}
!31 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !24, size: 32, flags: DIFlagObjectPointer)
!32 = !DISubprogram(name: "get<1>", linkageName: "_ZNH1C3getILi1EEEiOS_", scope: !24, file: !4, line: 57, type: !29, scopeLine: 57, flags: DIFlagPrototyped, spFlags: 0, templateParams: !16)
!33 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "D", file: !4, line: 60, size: 64, flags: DIFlagTypePassByValue, elements: !34, identifier: "_ZTS1D")
!34 = !{!35, !36, !37, !41}
!35 = !DIDerivedType(tag: DW_TAG_member, name: "w", scope: !33, file: !4, line: 61, baseType: !7, size: 32)
!36 = !DIDerivedType(tag: DW_TAG_member, name: "z", scope: !33, file: !4, line: 62, baseType: !7, size: 32, offset: 32)
!37 = !DISubprogram(name: "get<0>", linkageName: "_ZN1D3getILi0EEEii", scope: !33, file: !4, line: 64, type: !38, scopeLine: 64, flags: DIFlagPrototyped, spFlags: 0, templateParams: !13)
!38 = !DISubroutineType(types: !39)
!39 = !{!7, !40, !7}
!40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 32, flags: DIFlagArtificial | DIFlagObjectPointer)
!41 = !DISubprogram(name: "get<1>", linkageName: "_ZN1D3getILi1EEEii", scope: !33, file: !4, line: 65, type: !38, scopeLine: 65, flags: DIFlagPrototyped, spFlags: 0, templateParams: !16)
!42 = !{i32 1, !"NumRegisterParameters", i32 0}
!43 = !{i32 2, !"Debug Info Version", i32 3}
!44 = !{i32 1, !"wchar_size", i32 4}
!45 = !{!"clang version 22.0.0git012680fa"}
!46 = distinct !DISubprogram(name: "get<0U>", linkageName: "_ZSt3getILj0EEiSt6triple", scope: !19, file: !4, line: 104, type: !47, scopeLine: 104, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, templateParams: !50, retainedNodes: !49)
!47 = !DISubroutineType(types: !48)
!48 = !{!7, !18}
!49 = !{}
!50 = !{!51}
!51 = !DITemplateValueParameter(name: "I", type: !52, value: i32 0)
!52 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
!53 = !DILocalVariable(name: "p", arg: 1, scope: !46, file: !4, line: 104, type: !18)
!54 = !DILocation(line: 104, column: 31, scope: !46)
!55 = !DILocation(line: 104, column: 45, scope: !46)
!56 = !DILocation(line: 104, column: 36, scope: !46)
!57 = distinct !DISubprogram(name: "get<1U>", linkageName: "_ZSt3getILj1EEiSt6triple", scope: !19, file: !4, line: 105, type: !47, scopeLine: 105, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, templateParams: !58, retainedNodes: !49)
!58 = !{!59}
!59 = !DITemplateValueParameter(name: "I", type: !52, value: i32 1)
!60 = !DILocalVariable(name: "p", arg: 1, scope: !57, file: !4, line: 105, type: !18)
!61 = !DILocation(line: 105, column: 31, scope: !57)
!62 = !DILocation(line: 105, column: 45, scope: !57)
!63 = !DILocation(line: 105, column: 36, scope: !57)
!64 = distinct !DISubprogram(name: "get<2U>", linkageName: "_ZSt3getILj2EEiSt6triple", scope: !19, file: !4, line: 106, type: !47, scopeLine: 106, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, templateParams: !65, retainedNodes: !49)
!65 = !{!66}
!66 = !DITemplateValueParameter(name: "I", type: !52, value: i32 2)
!67 = !DILocalVariable(name: "p", arg: 1, scope: !64, file: !4, line: 106, type: !18)
!68 = !DILocation(line: 106, column: 31, scope: !64)
!69 = !DILocation(line: 106, column: 45, scope: !64)
!70 = !DILocation(line: 106, column: 36, scope: !64)
!71 = distinct !DISubprogram(name: "f", linkageName: "_Z1fv", scope: !4, file: !4, line: 109, type: !72, scopeLine: 109, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !49)
!72 = !DISubroutineType(types: !73)
!73 = !{!7}
!74 = !DILocalVariable(name: "a", scope: !71, file: !4, line: 110, type: !75)
!75 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !4, line: 39, size: 64, flags: DIFlagTypePassByValue, elements: !76, identifier: "_ZTS1A")
!76 = !{!77, !78}
!77 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !75, file: !4, line: 40, baseType: !7, size: 32)
!78 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !75, file: !4, line: 41, baseType: !7, size: 32, offset: 32)
!79 = !DILocation(line: 110, column: 5, scope: !71)
!80 = !DILocalVariable(name: "x1", scope: !71, file: !4, line: 111, type: !7)
!81 = !DILocation(line: 111, column: 9, scope: !71)
!82 = !DILocalVariable(name: "y1", scope: !71, file: !4, line: 111, type: !7)
!83 = !DILocation(line: 111, column: 13, scope: !71)
!84 = !DILocation(line: 111, column: 19, scope: !71)
!85 = !DILocalVariable(name: "x2", scope: !71, file: !4, line: 112, type: !7)
!86 = !DILocation(line: 112, column: 10, scope: !71)
!87 = !DILocalVariable(name: "y2", scope: !71, file: !4, line: 112, type: !7)
!88 = !DILocation(line: 112, column: 14, scope: !71)
!89 = !DILocation(line: 112, column: 9, scope: !71)
!90 = !DILocation(line: 113, column: 8, scope: !71)
!91 = !DILocalVariable(name: "z1", scope: !71, file: !4, line: 113, type: !92)
!92 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !93, size: 32)
!93 = !DIDerivedType(tag: DW_TAG_typedef, name: "type", scope: !94, file: !4, line: 88, baseType: !7)
!94 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "tuple_element<0U, B>", scope: !19, file: !4, line: 88, size: 8, flags: DIFlagTypePassByValue, elements: !49, templateParams: !95, identifier: "_ZTSSt13tuple_elementILj0E1BE")
!95 = !{!96, !97}
!96 = !DITemplateValueParameter(type: !52, value: i32 0)
!97 = !DITemplateTypeParameter(name: "T", type: !3)
!98 = !DILocation(line: 113, column: 9, scope: !71)
!99 = !DILocalVariable(name: "z2", scope: !71, file: !4, line: 113, type: !100)
!100 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !101, size: 32)
!101 = !DIDerivedType(tag: DW_TAG_typedef, name: "type", scope: !102, file: !4, line: 88, baseType: !7)
!102 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "tuple_element<1U, B>", scope: !19, file: !4, line: 88, size: 8, flags: DIFlagTypePassByValue, elements: !49, templateParams: !103, identifier: "_ZTSSt13tuple_elementILj1E1BE")
!103 = !{!104, !97}
!104 = !DITemplateValueParameter(type: !52, value: i32 1)
!105 = !DILocation(line: 113, column: 13, scope: !71)
!106 = !DILocalVariable(name: "array", scope: !71, file: !4, line: 114, type: !107)
!107 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 64, elements: !108)
!108 = !{!109}
!109 = !DISubrange(count: 2)
!110 = !DILocation(line: 114, column: 7, scope: !71)
!111 = !DILocalVariable(name: "a1", scope: !71, file: !4, line: 115, type: !7)
!112 = !DILocation(line: 115, column: 10, scope: !71)
!113 = !DILocalVariable(name: "a2", scope: !71, file: !4, line: 115, type: !7)
!114 = !DILocation(line: 115, column: 14, scope: !71)
!115 = !DILocation(line: 115, column: 9, scope: !71)
!116 = !DILocalVariable(name: "cmplx", scope: !71, file: !4, line: 116, type: !117)
!117 = !DIBasicType(name: "complex", size: 64, encoding: 128)
!118 = !DILocation(line: 116, column: 16, scope: !71)
!119 = !DILocalVariable(name: "c1", scope: !71, file: !4, line: 117, type: !7)
!120 = !DILocation(line: 117, column: 10, scope: !71)
!121 = !DILocalVariable(name: "c2", scope: !71, file: !4, line: 117, type: !7)
!122 = !DILocation(line: 117, column: 14, scope: !71)
!123 = !DILocation(line: 117, column: 9, scope: !71)
!124 = !DILocalVariable(name: "vctr", scope: !71, file: !4, line: 118, type: !125)
!125 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 64, flags: DIFlagVector, elements: !108)
!126 = !DILocation(line: 118, column: 7, scope: !71)
!127 = !DILocalVariable(name: "v1", scope: !71, file: !4, line: 119, type: !7)
!128 = !DILocation(line: 119, column: 10, scope: !71)
!129 = !DILocalVariable(name: "v2", scope: !71, file: !4, line: 119, type: !7)
!130 = !DILocation(line: 119, column: 14, scope: !71)
!131 = !DILocation(line: 119, column: 9, scope: !71)
!132 = !DILocation(line: 120, column: 8, scope: !71)
!133 = !DILocalVariable(name: "k", scope: !71, file: !4, line: 120, type: !134)
!134 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !135, size: 32)
!135 = !DIDerivedType(tag: DW_TAG_typedef, name: "type", scope: !136, file: !4, line: 88, baseType: !7)
!136 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "tuple_element<0U, std::triple>", scope: !19, file: !4, line: 88, size: 8, flags: DIFlagTypePassByValue, elements: !49, templateParams: !137, identifier: "_ZTSSt13tuple_elementILj0ESt6tripleE")
!137 = !{!96, !138}
!138 = !DITemplateTypeParameter(name: "T", type: !18)
!139 = !DILocation(line: 120, column: 9, scope: !71)
!140 = !DILocalVariable(name: "v", scope: !71, file: !4, line: 120, type: !141)
!141 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !142, size: 32)
!142 = !DIDerivedType(tag: DW_TAG_typedef, name: "type", scope: !143, file: !4, line: 88, baseType: !7)
!143 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "tuple_element<1U, std::triple>", scope: !19, file: !4, line: 88, size: 8, flags: DIFlagTypePassByValue, elements: !49, templateParams: !144, identifier: "_ZTSSt13tuple_elementILj1ESt6tripleE")
!144 = !{!104, !138}
!145 = !DILocation(line: 120, column: 12, scope: !71)
!146 = !DILocalVariable(name: "w", scope: !71, file: !4, line: 120, type: !147)
!147 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !148, size: 32)
!148 = !DIDerivedType(tag: DW_TAG_typedef, name: "type", scope: !149, file: !4, line: 88, baseType: !7)
!149 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "tuple_element<2U, std::triple>", scope: !19, file: !4, line: 88, size: 8, flags: DIFlagTypePassByValue, elements: !49, templateParams: !150, identifier: "_ZTSSt13tuple_elementILj2ESt6tripleE")
!150 = !{!151, !138}
!151 = !DITemplateValueParameter(type: !52, value: i32 2)
!152 = !DILocation(line: 120, column: 15, scope: !71)
!153 = !DILocation(line: 121, column: 8, scope: !71)
!154 = !DILocalVariable(name: "m", scope: !71, file: !4, line: 121, type: !155)
!155 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !156, size: 32)
!156 = !DIDerivedType(tag: DW_TAG_typedef, name: "type", scope: !157, file: !4, line: 88, baseType: !7)
!157 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "tuple_element<0U, C>", scope: !19, file: !4, line: 88, size: 8, flags: DIFlagTypePassByValue, elements: !49, templateParams: !158, identifier: "_ZTSSt13tuple_elementILj0E1CE")
!158 = !{!96, !159}
!159 = !DITemplateTypeParameter(name: "T", type: !24)
!160 = !DILocation(line: 121, column: 9, scope: !71)
!161 = !DILocalVariable(name: "n", scope: !71, file: !4, line: 121, type: !162)
!162 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !163, size: 32)
!163 = !DIDerivedType(tag: DW_TAG_typedef, name: "type", scope: !164, file: !4, line: 88, baseType: !7)
!164 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "tuple_element<1U, C>", scope: !19, file: !4, line: 88, size: 8, flags: DIFlagTypePassByValue, elements: !49, templateParams: !165, identifier: "_ZTSSt13tuple_elementILj1E1CE")
!165 = !{!104, !159}
!166 = !DILocation(line: 121, column: 12, scope: !71)
!167 = !DILocation(line: 122, column: 8, scope: !71)
!168 = !DILocalVariable(name: "s", scope: !71, file: !4, line: 122, type: !169)
!169 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !170, size: 32)
!170 = !DIDerivedType(tag: DW_TAG_typedef, name: "type", scope: !171, file: !4, line: 88, baseType: !7)
!171 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "tuple_element<0U, D>", scope: !19, file: !4, line: 88, size: 8, flags: DIFlagTypePassByValue, elements: !49, templateParams: !172, identifier: "_ZTSSt13tuple_elementILj0E1DE")
!172 = !{!96, !173}
!173 = !DITemplateTypeParameter(name: "T", type: !33)
!174 = !DILocation(line: 122, column: 9, scope: !71)
!175 = !DILocalVariable(name: "p", scope: !71, file: !4, line: 122, type: !176)
!176 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !177, size: 32)
!177 = !DIDerivedType(tag: DW_TAG_typedef, name: "type", scope: !178, file: !4, line: 88, baseType: !7)
!178 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "tuple_element<1U, D>", scope: !19, file: !4, line: 88, size: 8, flags: DIFlagTypePassByValue, elements: !49, templateParams: !179, identifier: "_ZTSSt13tuple_elementILj1E1DE")
!179 = !{!104, !173}
!180 = !DILocation(line: 122, column: 12, scope: !71)
!181 = !DILocation(line: 124, column: 6, scope: !71)
!182 = !DILocation(line: 127, column: 6, scope: !71)
!183 = !DILocation(line: 125, column: 6, scope: !71)
!184 = !DILocation(line: 129, column: 6, scope: !71)
!185 = !{i64 4}
!186 = !DILocation(line: 128, column: 6, scope: !71)
!187 = !DILocation(line: 132, column: 6, scope: !71)
!188 = !DILocation(line: 130, column: 6, scope: !71)
!189 = !DILocation(line: 134, column: 6, scope: !71)
!190 = !DILocation(line: 133, column: 6, scope: !71)
!191 = !DILocation(line: 137, column: 6, scope: !71)
!192 = !DILocation(line: 135, column: 6, scope: !71)
!193 = !DILocation(line: 139, column: 6, scope: !71)
!194 = !DILocation(line: 138, column: 6, scope: !71)
!195 = !DILocation(line: 142, column: 6, scope: !71)
!196 = !DILocation(line: 140, column: 6, scope: !71)
!197 = !DILocation(line: 144, column: 6, scope: !71)
!198 = !DILocation(line: 143, column: 6, scope: !71)
!199 = !DILocation(line: 147, column: 6, scope: !71)
!200 = !DILocation(line: 145, column: 6, scope: !71)
!201 = !DILocation(line: 149, column: 6, scope: !71)
!202 = !{i64 8}
!203 = !DILocation(line: 148, column: 6, scope: !71)
!204 = !DILocation(line: 152, column: 6, scope: !71)
!205 = !DILocation(line: 150, column: 6, scope: !71)
!206 = !DILocation(line: 123, column: 3, scope: !71)
!207 = distinct !DISubprogram(name: "get<0>", linkageName: "_ZN1B3getILi0EEEiv", scope: !3, file: !4, line: 48, type: !10, scopeLine: 48, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, templateParams: !13, declaration: !9, retainedNodes: !49)
!208 = !DILocalVariable(name: "this", arg: 1, scope: !207, type: !209, flags: DIFlagArtificial | DIFlagObjectPointer)
!209 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 32)
!210 = !DILocation(line: 0, scope: !207)
!211 = !DILocation(line: 48, column: 36, scope: !207)
!212 = !DILocation(line: 48, column: 29, scope: !207)
!213 = distinct !DISubprogram(name: "get<1>", linkageName: "_ZN1B3getILi1EEEiv", scope: !3, file: !4, line: 49, type: !10, scopeLine: 49, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, templateParams: !16, declaration: !15, retainedNodes: !49)
!214 = !DILocalVariable(name: "this", arg: 1, scope: !213, type: !209, flags: DIFlagArtificial | DIFlagObjectPointer)
!215 = !DILocation(line: 0, scope: !213)
!216 = !DILocation(line: 49, column: 36, scope: !213)
!217 = !DILocation(line: 49, column: 29, scope: !213)
!218 = distinct !DISubprogram(name: "get<0>", linkageName: "_ZNH1C3getILi0EEEiOS_", scope: !24, file: !4, line: 56, type: !29, scopeLine: 56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, templateParams: !13, declaration: !28, retainedNodes: !49)
!219 = !DILocalVariable(name: "self", arg: 1, scope: !218, file: !4, line: 56, type: !220, flags: DIFlagObjectPointer)
!220 = !DIDerivedType(tag: DW_TAG_rvalue_reference_type, baseType: !24, size: 32)
!221 = !DILocation(line: 56, column: 34, scope: !218)
!222 = !DILocation(line: 56, column: 49, scope: !218)
!223 = !DILocation(line: 56, column: 54, scope: !218)
!224 = !DILocation(line: 56, column: 42, scope: !218)
!225 = distinct !DISubprogram(name: "get<1>", linkageName: "_ZNH1C3getILi1EEEiOS_", scope: !24, file: !4, line: 57, type: !29, scopeLine: 57, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, templateParams: !16, declaration: !32, retainedNodes: !49)
!226 = !DILocalVariable(name: "self", arg: 1, scope: !225, file: !4, line: 57, type: !220, flags: DIFlagObjectPointer)
!227 = !DILocation(line: 57, column: 34, scope: !225)
!228 = !DILocation(line: 57, column: 49, scope: !225)
!229 = !DILocation(line: 57, column: 54, scope: !225)
!230 = !DILocation(line: 57, column: 42, scope: !225)
!231 = distinct !DISubprogram(name: "get<0>", linkageName: "_ZN1D3getILi0EEEii", scope: !33, file: !4, line: 64, type: !38, scopeLine: 64, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, templateParams: !13, declaration: !37, retainedNodes: !49)
!232 = !DILocalVariable(name: "this", arg: 1, scope: !231, type: !233, flags: DIFlagArtificial | DIFlagObjectPointer)
!233 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 32)
!234 = !DILocation(line: 0, scope: !231)
!235 = !DILocalVariable(name: "unused", arg: 2, scope: !231, file: !4, line: 64, type: !7)
!236 = !DILocation(line: 64, column: 29, scope: !231)
!237 = !DILocation(line: 64, column: 46, scope: !231)
!238 = !DILocation(line: 64, column: 39, scope: !231)
!239 = distinct !DISubprogram(name: "get<1>", linkageName: "_ZN1D3getILi1EEEii", scope: !33, file: !4, line: 65, type: !38, scopeLine: 65, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, templateParams: !16, declaration: !41, retainedNodes: !49)
!240 = !DILocalVariable(name: "this", arg: 1, scope: !239, type: !233, flags: DIFlagArtificial | DIFlagObjectPointer)
!241 = !DILocation(line: 0, scope: !239)
!242 = !DILocalVariable(name: "unused", arg: 2, scope: !239, file: !4, line: 65, type: !7)
!243 = !DILocation(line: 65, column: 29, scope: !239)
!244 = !DILocation(line: 65, column: 46, scope: !239)
!245 = !DILocation(line: 65, column: 39, scope: !239) |
Thanks! that was useful. Put up fix here: #160300 |
…compatible compiler version Requires a compiler with the changes in #122265
… check for compatible compiler version Requires a compiler with the changes in llvm/llvm-project#122265
…compatible compiler version Requires a compiler with the changes in llvm#122265
When we generate the debug-info for a
VarDeclwe try to determine whether it was introduced as part of a structure binding (aka a "holding var"). If it was then we don't mark it asartificial.The heuristic to determine a holding var uses
IgnoreUnlessSpelledInSourceto unwrap theVarDeclinitializer until we hit aDeclRefExprthat refers to aDecomposition. For "tuple-like decompositions", Clang will generate a call to atemplate<size_t I> Foo get(Bar)function that retrieves theIthelement from the tuple-like structure. If that function is a member function, we get an AST that looks as follows:IgnoreUnlessSpelledInSourcehappily unwraps this down to theDeclRefExpr. However, when thegethelper is a free function (which it is forstd::pairin libc++ for example), then the AST is:IgnoreUnlessSpelledInSourcedoesn't unwrap thisCallExpr, so we incorrectly mark the binding asartificialin debug-info.This patch adjusts
IgnoreUnlessSpelledInSourceso it unwraps implicitCallExprs. It's almost identical to how we treat implicit constructor calls (unfortunately the code can't quite be re-used because aCXXConstructExpris-not aCallExpr, and we checkisElidable, which doesn't exist for regular function calls. So I added a newIgnoreImplicitCallSingleStep).Fixes #122028